﻿using Dapper;
using DapperExtensions;
using System;
using System.Collections.Generic;
using System.Data;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Linq;
using DapperExtensions.Mapper;
using ZLWeb.WebApiEntity;



namespace DapperWrapper
{
    /// <summary>
    /// Dapper基本操作的封装，仓库
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    public class Respository<TEntity> : IRespository where TEntity: class, new()
    {
        protected  IDbContext _context;

       protected ErrorModel error = new ErrorModel();

        public Respository()
        {
            this._context = new SourceContext();
        }

        public ClassMapper<TEntity> GetMap()
        {
            return DapperExtensions.DapperExtensions.GetMap<TEntity>() as ClassMapper<TEntity>;
        }

       
        /// <summary>
        /// 添加实体
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        protected  dynamic Add(TEntity item)
        {
            Func<IDbConnection, IDbTransaction, object, object> action = null;
            UnitOfWork currentUnitOfWork = UnitOfWork.GetCurrentUnitOfWork();
            if (currentUnitOfWork == null || (currentUnitOfWork != null && currentUnitOfWork.Option == UnitOfWorkOption.Suppress))
            {
                IDbConnection writeConnection = this._context.GetConnection();
                object obj2 = null;
                try
                {
                    writeConnection.Open();
                     obj2 = writeConnection.Insert<TEntity>(item, null, null);
                }
                catch (Exception ex)
                {
                    throw new SpException("", ex);
                }
                finally
                {
                    writeConnection.Close();
                }
                return obj2;
            }
            if (currentUnitOfWork != null)
            {
                if (action == null)
                {
                    action = delegate(IDbConnection conn, IDbTransaction tran, object oitem)
                    {
                        return conn.Insert<TEntity>((TEntity)oitem, tran, null);
                    };
                }
                IClassMapper mapper = DapperExtensions.DapperExtensions.GetMapIncludeForeignKey<TEntity>();
                var properties = mapper.Properties;
                IPropertyMap propertyMap =
                    properties.Where(p => p.Name == "Id").SingleOrDefault(p => p.ColumnName.IndexOf('.') != -1);
                currentUnitOfWork.ChangeOperator(action, this._context, item, propertyMap);
                return null;
            }
            return null;
        }

        /// <summary>
        /// 执行SQL,映射实体
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        protected  void Execute(string sql, dynamic parameters = null)
        {
            Func<IDbConnection, IDbTransaction, object, object> action = null;
            UnitOfWork currentUnitOfWork = UnitOfWork.GetCurrentUnitOfWork();
            if (currentUnitOfWork == null || (currentUnitOfWork != null && currentUnitOfWork.Option == UnitOfWorkOption.Suppress))
            {
                IDbConnection writeConnection = this._context.GetConnection();
                try
                {
                    writeConnection.Open();
                    SqlMapper.Execute(writeConnection, sql, (dynamic)parameters);
                }
                catch (Exception ex)
                {
                    throw new SpException("", ex);
                }
                finally
                {
                    writeConnection.Close();
                }
            }
            if (currentUnitOfWork != null)
            {
                if (action == null)
                {
                    action = (conn, tran,entity) => SqlMapper.Execute(conn, sql, (dynamic) parameters, tran);
                }
                currentUnitOfWork.ChangeOperator(action, this._context);
            }
        }

        /// <summary>
        /// 获取所有实体
        /// </summary>
        /// <returns></returns>
        protected  IEnumerable<TEntity> GetAll()
        {
            IEnumerable<TEntity> list = null;
            this.NoLockInvoke(delegate (IDbConnection conn, IDbTransaction tran) {
                list = conn.GetList<TEntity>(null, null, tran, null, false);
            });
            return list;
        }

        /// <summary>
        /// 获取单个标量值
        /// </summary>
        /// <typeparam name="U"></typeparam>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        protected  U? GetDataByScalar<U>(string sql, dynamic parameters = null) where U : struct
        {
            if (this.IsChangeSqlString(sql))
            {
                throw new Exception("sql语句错误,执行的是查询方法,但SQL语句包含更新代码,SQL语句:" + sql);
            }
            Nullable<U> value = null;
            Type type = typeof (U);
            this.NoLockInvoke(delegate (IDbConnection conn, IDbTransaction tran)
                                  {
                                      IEnumerable<object> values = SqlMapper.Query(conn, type, sql, (dynamic)parameters, tran);
                                      value = (Nullable<U>)values.SingleOrDefault();
                                  });
            return value;
        }
        protected string GetDataByScalarString(string sql, dynamic parameters = null) 
        {
            if (this.IsChangeSqlString(sql))
            {
                throw new Exception("sql语句错误,执行的是查询方法,但SQL语句包含更新代码,SQL语句:" + sql);
            }
            string value = null;
            Type type = typeof(string);
            this.NoLockInvoke(delegate(IDbConnection conn, IDbTransaction tran)
            {
                IEnumerable<object> values = SqlMapper.Query(conn, type, sql, (dynamic)parameters, tran);
                value = values.SingleOrDefault().ToString();
            });
            return value;
        }
        /// <summary>
        /// 根据id获取实体
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        protected  TEntity GetEntityById(dynamic id)
        {
            TEntity entity = default(TEntity);
            this.NoLockInvoke(delegate (IDbConnection conn, IDbTransaction tran) {
                entity = (TEntity)DapperExtensions.DapperExtensions.Get<TEntity>(conn, id, tran);
            });
            return entity;
        }

        /// <summary>
        /// 根据SQL获取实体列表
        /// </summary>
        /// <typeparam name="U"></typeparam>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public  IEnumerable<U> GetList<U>(string sql, dynamic parameters = null)
        {
            if (this.IsChangeSqlString(sql))
            {
                throw new Exception("sql语句错误,执行的是查询方法,但SQL语句包含更新代码,SQL语句:" + sql);
            }
            IEnumerable<U> list = null;
            this.NoLockInvoke(delegate(IDbConnection conn, IDbTransaction tran)
            {
                list = (IEnumerable<U>)SqlMapper.Query<U>(conn, sql, (dynamic)parameters, tran);
            });
            return list;
        }

        protected  IEnumerable<TEntity> GetList(string sql, dynamic parameters = null)
        {
            if (this.IsChangeSqlString(sql))
            {
                throw new Exception("sql语句错误,执行的是查询方法,但SQL语句包含更新代码,SQL语句:" + sql);
            }
            IEnumerable<TEntity> list = null;
            this.NoLockInvoke(delegate(IDbConnection conn, IDbTransaction tran)
            {
                list = (IEnumerable<TEntity>)SqlMapper.Query<TEntity>(conn, sql, (dynamic)parameters, tran);
            });
            return list;
        }

        /// <summary>
        /// 判断是否是改变数据库的SQL dcl
        /// </summary>
        /// <param name="sql"></param>
        /// <returns></returns>
        protected  bool IsChangeSqlString(string sql)
        {
            sql = sql.Trim().ToLower();
            return (Regex.IsMatch(sql, "^(update|delete|insert|create|alter|drop|truncate)") || Regex.IsMatch(sql, @"^(select)\s+(into)\s"));
        }

        /// <summary>
        /// 更新实体
        /// </summary>
        /// <param name="item"></param>
        protected  void Modify(TEntity item)
        {
            Func<IDbConnection, IDbTransaction,object, object> action = null;
            UnitOfWork currentUnitOfWork = UnitOfWork.GetCurrentUnitOfWork();
            if (currentUnitOfWork == null || (currentUnitOfWork != null && currentUnitOfWork.Option == UnitOfWorkOption.Suppress))
            {
                IDbConnection writeConnection = this._context.GetConnection();
                try
                {
                    writeConnection.Open();
                    writeConnection.Update<TEntity>(item, null, null);
                }
                catch (Exception ex)
                {
                    throw new SpException("", ex);
                }
                finally
                {
                    writeConnection.Close();
                }
            }
            else if (currentUnitOfWork != null)
            {
                if (action == null)
                {
                    action = (conn, tran, entity) => conn.Update<TEntity>(item, tran, null);
                }
                currentUnitOfWork.ChangeOperator(action, this._context);
            }
        }

        protected void NoLockInvoke(Action<IDbConnection, IDbTransaction> action)
        {
            using (IDbConnection connection = this._context.GetConnection())
            {
                try
                {
                    connection.Open();
                    using (IDbTransaction transaction = connection.BeginTransaction())
                    {
                        action(connection, transaction);
                        transaction.Commit();
                    }
                }
                catch (Exception ex)
                {
                    throw new SpException("", ex);
                }
                finally
                {
                    connection.Close();
                }
            }
        }


        /// <summary>
        /// 删除实体
        /// </summary>
        /// <param name="item"></param>
        protected  void Remove(TEntity item)
        {
            Func<IDbConnection, IDbTransaction, object, object> action = null;
            UnitOfWork currentUnitOfWork = UnitOfWork.GetCurrentUnitOfWork();
            if (currentUnitOfWork == null || (currentUnitOfWork != null && currentUnitOfWork.Option == UnitOfWorkOption.Suppress))
            {
                IDbConnection writeConnection = this._context.GetConnection();
                try
                {
                    writeConnection.Open();
                    writeConnection.Delete<TEntity>(item, null, null);
                }
                catch (Exception ex)
                {
                    throw new SpException("", ex);
                }
                finally
                {
                    writeConnection.Close();
                }
            }
            else if (currentUnitOfWork != null)
            {
                if (action == null)
                {
                    action = (conn, tran,entity) => conn.Delete<TEntity>(item, tran, null);
                }
                currentUnitOfWork.ChangeOperator(action, this._context);
            }
        }

        /// <summary>
        /// 临时表DDL-》插入数据-》存储过程-》返回结果
        /// </summary>
        /// <typeparam name="TEntity">实体</typeparam>
        /// <param name="item">对象</param>
        /// <param name="ProcName">存储过程名称</param>
        /// <param name="tempdbname">临时表名称</param>
        /// <param name="IsInsertEntity">是否插入实体</param>
        /// <param name="parameters">传入参数</param>
        /// <param name="ChangedTableName">强制修改表名</param>
        /// <returns>返回执行结果</returns>
        protected IEnumerable<TEntity> ExecuteProc(string ProcName, object parameters = null) 
        {
            return InnerExecuteProc<TEntity>(null, ProcName, null, false, parameters, null);
        }


        /// <summary>
        /// 临时表DDL-》插入数据-》存储过程-》返回结果
        /// </summary>
        /// <typeparam name="TEntity">实体</typeparam>
        /// <param name="item">对象</param>
        /// <param name="ProcName">存储过程名称</param>
        /// <param name="tempdbname">临时表名称</param>
        /// <param name="IsInsertEntity">是否插入实体</param>
        /// <param name="parameters">传入参数</param>
        /// <param name="ChangedTableName">强制修改表名</param>
        /// <returns>返回执行结果</returns>
        protected IEnumerable<TEntity> ExecuteProcWithTemptable<TEntity>(TEntity item, string ProcName, string tempdbname, bool IsInsertEntity = false, object parameters = null,string ChangedTableName=null) where TEntity : class
        {
            if (item == null)
            {
                return null;
            }
            Action<IDbConnection> Insertfun = (writeConnection) =>
            {
                writeConnection.CreateTable(item, tempdbname);
                if (IsInsertEntity)
                {
                    if (ChangedTableName != null) GetMap().TableName = ChangedTableName;
                    writeConnection.Insert<TEntity>(item, null, null);
                }
            };
            return InnerExecuteProc<TEntity>(Insertfun, ProcName, tempdbname, IsInsertEntity, parameters, ChangedTableName);
        }
        /// <summary>
        /// 临时表DDL-》插入数据-》存储过程-》返回结果
        /// </summary>
        /// <typeparam name="TEntity">实体</typeparam>
        /// <param name="item">对象</param>
        /// <param name="ProcName">存储过程名称</param>
        /// <param name="tempdbname">临时表名称</param>
        /// <param name="IsInsertEntity">是否插入实体</param>
        /// <param name="ChangedTableName">强制修改表名</param>
        /// <returns>返回执行结果</returns>
        protected IEnumerable<TEntity> ExecuteProcWithTemptable<TEntity>(TEntity item, string ProcName, string tempdbname, bool IsInsertEntity = false,  string ChangedTableName = null) where TEntity : class
        {
            if (item == null)
            {
                return null;
            }
            Action<IDbConnection> Insertfun = (writeConnection) =>
            {
                writeConnection.CreateTable(item, tempdbname);
                if (IsInsertEntity)
                {
                    if (ChangedTableName != null) GetMap().TableName = ChangedTableName;
                    writeConnection.Insert<TEntity>(item, null, null);
                }
            };
            return InnerExecuteProc<TEntity>(Insertfun, ProcName, tempdbname, IsInsertEntity, null, ChangedTableName);
        }

        /// <summary>
        /// 临时表DDL-》插入数据-》存储过程-》返回结果
        /// </summary>
        /// <typeparam name="TEntity[]"></typeparam>
        /// <param name="item"></param>
        /// <param name="ProcName"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        protected IEnumerable<TEntity> ExecuteProcWithTemptableList<TEntity>(List<TEntity> items, string ProcName, string tempdbname, bool IsInsertEntity = false, object parameters = null, string ChangedTableName = null) where TEntity : class
        {
            if (items == null)
            {
                return null;
            }
            Action<IDbConnection> Insertfun = (writeConnection) =>
            {
                writeConnection.CreateTable(items.FirstOrDefault(), tempdbname);
                if (IsInsertEntity)
                {
                    if (ChangedTableName != null) GetMap().TableName = ChangedTableName;
                    foreach (var item in items)
                    {
                        writeConnection.Insert<TEntity>(item, null, null);
                    }
                }
            };
            return InnerExecuteProc<TEntity>(Insertfun, ProcName, tempdbname, IsInsertEntity, parameters, ChangedTableName);
           
        }

        private IEnumerable<TEntity> InnerExecuteProc<TEntity>(Action<IDbConnection> Insertfun, string ProcName, string tempdbname, bool IsInsertEntity, object parameters, string ChangedTableName) where TEntity : class
        {
            Func<IDbConnection, IDbTransaction, object, object> action = null;
            UnitOfWork currentUnitOfWork = UnitOfWork.GetCurrentUnitOfWork();
            if (currentUnitOfWork == null || (currentUnitOfWork != null && currentUnitOfWork.Option == UnitOfWorkOption.Suppress))
            {
                IDbConnection writeConnection = this._context.GetConnection();
                try
                {
                    writeConnection.Open();
                    if (Insertfun!=null)
                    Insertfun(writeConnection);
                    if(parameters==null) parameters=new object();
                    using (IDbTransaction transaction = writeConnection.BeginTransaction())
                    {
                        IEnumerable<ErrorModel> errors = (IEnumerable<ErrorModel>)SqlMapper.Query<ErrorModel>(writeConnection, ProcName, (dynamic)parameters, transaction, commandType: CommandType.StoredProcedure);
                        error = errors.FirstOrDefault();
                        if (error == null)
                        {
                            transaction.Commit();
                        }
                        else
                        {
                            transaction.Rollback();
                        }
                    }
                    if (Insertfun == null) return null;
                    if (ChangedTableName != null) GetMap().TableName = ChangedTableName;
                    IEnumerable<TEntity> list = (IEnumerable<TEntity>)writeConnection.GetList<TEntity>(null, null, null, null, false);
                    return list;
                }
                catch (Exception ex)
                {
                    throw new SpException("", ex);
                }
                finally
                {
                    writeConnection.Close();
                }
            }
            return null;
        }

        /// <summary>
        /// 临时表DDL-》插入数据-》存储过程-》返回结果
        /// </summary>
        /// <typeparam name="TEntity[]"></typeparam>
        /// <param name="item"></param>
        /// <param name="ProcName"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public Action<IDbConnection> ExecuteProcAddParams<TEntity>(IEnumerable<TEntity> items, string tempdbname, bool IsInsertEntity = false) where TEntity : class
        {
            if (items == null)
            {
                return null;
            }
            Action<IDbConnection> Insertfun = (writeConnection) =>
            {
                writeConnection.CreateTable(items.FirstOrDefault(), tempdbname);
                if (IsInsertEntity)
                {
                    foreach (var item in items)
                    {
                        writeConnection.Insert<TEntity>(item, null, null);
                    }
                }
            };
            return Insertfun;
        }

        /// <summary>
        /// 临时表DDL-》插入数据-》存储过程-》返回结果
        /// </summary>
        /// <typeparam name="TEntity[]"></typeparam>
        /// <param name="item"></param>
        /// <param name="ProcName"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public Action<IDbConnection> ExecuteProcAddParam<TEntity>(TEntity item, string tempdbname, bool IsInsertEntity = false) where TEntity : class
        {
            if (item == null)
            {
                return null;
            }
            Action<IDbConnection> Insertfun = (writeConnection) =>
            {
                writeConnection.CreateTable(item, tempdbname);
                if (IsInsertEntity)
                {
                    writeConnection.Insert<TEntity>(item, null, null);
                }
            };
            return Insertfun;
        }

        public void ExecuteProc(List<Action<IDbConnection>> Insertfuns, string ProcName, object parameters = null, Action<IDbConnection> Returnlist = null) 
        {
            Func<IDbConnection, IDbTransaction, object, object> action = null;
            UnitOfWork currentUnitOfWork = UnitOfWork.GetCurrentUnitOfWork();
            if (currentUnitOfWork == null || (currentUnitOfWork != null && currentUnitOfWork.Option == UnitOfWorkOption.Suppress))
            {
                IDbConnection writeConnection = this._context.GetConnection();
                try
                {
                    writeConnection.Open();
                    foreach (var Insertfun in Insertfuns)
                    {
                        Insertfun(writeConnection);
                    }
                    if (parameters == null) parameters = new object();
                    using (IDbTransaction transaction = writeConnection.BeginTransaction())
                    {
                        IEnumerable<ErrorModel> errors =
                            (IEnumerable<ErrorModel>)
                                SqlMapper.Query<ErrorModel>(writeConnection, ProcName, (dynamic) parameters, transaction,
                                    commandType: CommandType.StoredProcedure);
                        error = errors.FirstOrDefault();
                        if (error == null)
                        {
                            transaction.Commit();
                        }
                        else
                        {
                            transaction.Rollback();
                        }
                    }
                    if (Returnlist != null)
                    {
                        Returnlist(writeConnection);
                    }
                }
                catch (Exception ex)
                {
                    throw new SpException("", ex);
                }
                finally
                {
                    writeConnection.Close();
                }
            }
        }


        ErrorModel IRespository.error
        {
            get { return error; }
            set { error = value; }
        }
    }
}

